#define _GNU_SOURCE
#include <stdlib.h>
#include <assert.h>
#include <strings.h>

#include <visual.h>
#ifdef HAVE_SDL
#include <visual_sdl.h>
#endif /* HAVE_SDL */

#include <visual_ppm.h>/* our default visual layer */
#include <debug.h>

static struct visual_ops visual_ops = {
  .quit    = NULL,
  .init    = NULL,
  .update  = NULL,
  .waitkey = NULL,
  .clear   = NULL,
  .fill    = NULL,
  .display = NULL,
  .save    = NULL,
};

void
visual_change_layer( const struct visual_ops *layer_ops )
{
  assert( layer_ops != NULL );
  assert( layer_ops->quit != NULL );
  assert( layer_ops->init != NULL );
  assert( layer_ops->update != NULL );
  assert( layer_ops->waitkey != NULL );
  assert( layer_ops->clear != NULL );
  assert( layer_ops->fill != NULL );
  assert( layer_ops->display != NULL );
  assert( layer_ops->save != NULL );

  visual_ops = *layer_ops;
}



#define CALL_RET_INT( op, args... )                                           \
  do { if (visual_ops.op != NULL )                                            \
       return visual_ops.op( args );                                        \
       else err( "You should define a visual layer with " #op "() first.\n" );\
       return -1;                                                             \
  } while ( 0 )

#define CALL_RET_VOID( op, args... )                                          \
  do { if (visual_ops.op != NULL )                                            \
         visual_ops.op( args );                                             \
       else err( "You should define a visual layer with " #op "() first.\n" );\
  } while ( 0 )



/**
 * Quit engine.
 */
void
visual_quit( void )
{
  CALL_RET_VOID( quit );
}

/**
 * Initialize video device using the given dimensions.
 *
 * @param w screen width
 * @param h screen height
 *
 * @return 0 on succes, error otherwise.
 */
int
visual_init( uint16_t w, uint16_t h )
{
  char *envopt = getenv( "VISUAL" );

  if ( visual_ops.init == NULL )
    {
      if ( envopt == NULL )
	{
#ifdef HAVE_SDL
    visual_change_layer_sdl(); /* sdl is our default visual layer */
#else
    visual_change_layer_ppm(); /* ppm is our default visual layer */
#endif
	}
      else
	{
	  if ( strcasecmp( envopt, "ppm" ) == 0 )
	    visual_change_layer_ppm();
#ifdef HAVE_SDL
	  else if ( strcasecmp( envopt, "sdl" ) == 0 )
	    visual_change_layer_sdl();
#endif
	  else
	    visual_change_layer_ppm();
	}
    }

  CALL_RET_INT( init, w, h );
  return 0;
}

/**
 * Update screen.
 *
 * @return 0 on success, error otherwise.
 */
int
visual_update( void )
{
  CALL_RET_INT( update );
  return 0;
}

/**
 * Wait for key and return key symbol as described in SDLKey(3).
 *
 * It also handle program exit and do screen refreshes when
 * required.
 *
 * @return key symbol or 0 on error.
 */
int
visual_waitkey( void )
{
  CALL_RET_INT( waitkey );
  return 0;
}


/**
 * Clear (blanks) screen.
 *
 * @return 0 on success, error otherwise.
 */
int
visual_clear( void )
{
  CALL_RET_INT( clear );
  return 0;
}


/**
 * Fill rectangular screen region with the given color.
 *
 * @param color what color to paint.
 * @param rectan
 *
 * @return 0 on success, error otherwise.
 */
int
visual_fill( color_t color, rect_t rect )
{
  CALL_RET_INT( fill, color, rect );
  return 0;
}


/**
 * Display the surface at the given position
 *
 * @param surface what to display
 * @param pos where to display
 *
 * @return 0 on success, error otherwise.
 */
int
visual_display( surface_t *surface, coord_t pos )
{
  CALL_RET_INT( display, surface, pos );
  return 0;
}

/**
 * Save the display to a bitmap image.
 *
 * @param filename where to save the file.
 *
 * @return 0 on success, error otherwise.
 */
int
visual_save( const char *filename )
{
  CALL_RET_INT( save, filename );
  return 0;
}
